﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Linq;

namespace DataStore
{
    public class FileDataStore:IDisposable
    {
        public long DataCount { get; set; }
      
        public long MaxDataBlockSize { get; set; }

        public string BasePath { get; private set; }

        public string FileNamePrefix { get; private set; }

        private const string IndexFileExt = ".idx";
        private const string DataFileExt = ".dat";
        private const string SequenceMask = "0000000000";
        private List<DataBlock> DataBlocks = new List<DataBlock>();
        private DataBlock CurrentWriteDataBlock = null;
        private int BeginDataBlockFileIndex = 0;
        private int BeginDataIndex = 0;
        

        public FileDataStore(string basePath, string fileNamePrefix, long maxDataBlockSize = DataBlock.DefaultMaxDataSize)
        {
            BasePath = BasePath;
            FileNamePrefix = fileNamePrefix;
            MaxDataBlockSize = maxDataBlockSize;
            LoadDataBlocks();
        }

        public Span<byte> ReadData(long index)
        {
            if (index > DataCount) throw new ArgumentOutOfRangeException();

            return null;
        }

        public void WriteData(Span<byte> data)
        {
            if (CurrentWriteDataBlock.WriteData(data) == false)
            {
                var index = DataBlocks.Count;
                var indexFileName = Path.Combine(BasePath, FileNamePrefix + index.ToString(SequenceMask) + IndexFileExt);
                var dataFileName = Path.Combine(BasePath, FileNamePrefix + index.ToString(SequenceMask) + DataFileExt);
                CurrentWriteDataBlock = new DataBlock(dataFileName, indexFileName, true, MaxDataBlockSize);
                CurrentWriteDataBlock.WriteData(data);
            }
            DataCount++;
        }

        public void DeleteDataBlock(int dataBlockIndex)
        {
            for (var i = BeginDataBlockFileIndex; i < dataBlockIndex; i++)
            {
                var datablock = DataBlocks[i - BeginDataBlockFileIndex];
                datablock.Close();
                DataBlocks.RemoveAt(i - BeginDataBlockFileIndex);
                var filenName = GetDataBlockFileName(i);
                File.Delete(filenName.IndexFileName);
                File.Delete(filenName.DataFileName);
            }
        }

        private void LoadDataBlocks()
        {
            var indexFiles = GetDataBlockFiles(FileNamePrefix + "*" + IndexFileExt);
            if (indexFiles.Length > 0)
            {
                var sequenceStart = Path.Combine(BasePath, FileNamePrefix ).Length;
                BeginDataBlockFileIndex = Convert.ToInt32(indexFiles.Min(r => r).AsSpan().Slice(sequenceStart, SequenceMask.Length).ToString());
                var dataFiles = GetDataBlockFiles(FileNamePrefix + "*" + DataFileExt);
                for (var i = BeginDataBlockFileIndex; ; i++)
                {
                    var fileName = GetDataBlockFileName(i);
                    if (indexFiles.Contains(fileName.IndexFileName) && dataFiles.Contains(fileName.DataFileName))
                    {
                        CurrentWriteDataBlock = new DataBlock(fileName.DataFileName, fileName.IndexFileName, false, MaxDataBlockSize);
                        DataCount += CurrentWriteDataBlock.DataCount;
                        DataBlocks.Add(CurrentWriteDataBlock);
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }

        private (string IndexFileName, string DataFileName) GetDataBlockFileName(int index)
        {
            var indexFileName = Path.Combine(BasePath, FileNamePrefix + index.ToString(SequenceMask) + IndexFileExt);
            var dataFileName = Path.Combine(BasePath, FileNamePrefix + index.ToString(SequenceMask) + DataFileExt);
            return (indexFileName, dataFileName);
        }

        private string[] GetDataBlockFiles( string searchPattern)
        {
            string[] fileNames;
            if (Directory.Exists(BasePath))
            {
                fileNames = Directory.GetFiles(BasePath, searchPattern);
            }
            else
            {
                Directory.CreateDirectory(BasePath);
                fileNames = new string[0];
            }
            return fileNames;
        }


        #region 析构函数

        private bool isdisposed;

        public void Close()
        {
            Dispose();
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (isdisposed == false)
            {
                if (disposing)  //释放托管资源
                {
                     foreach(var dataBlock in DataBlocks)
                    {
                        dataBlock.Dispose();
                    }
                }
                //释放非托管资源
                //do something

                isdisposed = true;
            }
        }

        ~FileDataStore()
        {
            Dispose(false);
        }

        #endregion
    }
}
